home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / fmodla13.zip / TALK.ZIP / TALK.MOD < prev    next >
Text File  |  1987-12-20  |  16KB  |  527 lines

  1. MODULE Talk;
  2.  
  3. (* (C) Copyright 1987 Fitted Software Tools. All rights reserved.
  4.  
  5.     This module is part of the example multitasking communications program
  6.     provided with the Fitted Software Tools' Modula-2 development system.
  7.  
  8.     Registered users may use this program as is, or they may modify it to
  9.     suit their needs or as an exercise.
  10.  
  11.     If you develop interesting derivatives of this program and would like
  12.     to share it with others, we encourage you to upload a copy to our BBS.
  13. *)
  14.  
  15.  
  16. (*$L+*)
  17.  
  18. IMPORT Terminal, Display;
  19. FROM SYSTEM     IMPORT  ASSEMBLER, ADDRESS, ADR;
  20. FROM System     IMPORT  GetArg, TermProcedure, Terminate,
  21.                         GetVector, ResetVector;
  22. FROM InOut      IMPORT  WriteString, WriteCard, ReadCard, WriteLn;
  23. FROM Strings    IMPORT  CompareStr, Concat;
  24. FROM Keyboard   IMPORT  F10, KeyPressed, GetKeyCh;
  25. FROM RS232      IMPORT  RS232Input,
  26.                         Init, ResetPars, GetCom, PutCom, XON, XOFF;
  27. FROM ASCII      IMPORT  FF, CR, DEL, BEL, LF, BS, HT, ESC, CtrlS, CtrlQ;
  28. FROM NumberConversion
  29.                 IMPORT  StringToCard;
  30. FROM Display    IMPORT  ScrollUp, DisplayLine, Goto;
  31. FROM Windows    IMPORT  Window, OpenWindow, CloseCurWindow;
  32. FROM Menu       IMPORT  PopMenu;
  33. FROM XModem     IMPORT  SendFile, ReceiveFile;
  34. FROM Files      IMPORT  NORMAL, READ, Open, Create, Close, Read, Write;
  35. FROM Kernel     IMPORT  SignalHeader, LockHeader, InitSignal, InitLock,
  36.                         NewProcess, Wait, WaitIO, Signal, Lock, Unlock;
  37.  
  38. CONST
  39.     comBuffSize = 2048;
  40.     attrNormal  = 07H;
  41.     attrReverse = 70H;
  42.  
  43. VAR Capturing       :BOOLEAN;       (* capture file open *)
  44.     Sending         :BOOLEAN;       (* sending a file *)
  45.     Xon             :BOOLEAN;       (* XON/XOFF enabled *)
  46.  
  47.     DisplayLock     :LockHeader;    (* only one process may write
  48.                                         to the screen at any time *)
  49.     SendLock        :LockHeader;    (* to suspend SendFile when we
  50.                                         receive an XOFF *)
  51.  
  52. (*** Program command and reconfiguration ***)
  53.  
  54. VAR
  55.     port    :CARDINAL;
  56.     baud    :CARDINAL;
  57.     parity  :BOOLEAN;
  58.     evenp   :BOOLEAN;
  59.     bits    :CARDINAL;
  60.  
  61.  
  62. PROCEDURE Command;
  63. VAR cmdstr  :ARRAY [0..255] OF CHAR;
  64.     cmd     :CARDINAL;
  65.     fn      :ARRAY [0..65] OF CHAR;
  66.     done    :BOOLEAN;
  67.     w       :Window;
  68.  
  69.     PROCEDURE XModemCom;    (* config COM for XModem *)
  70.     BEGIN
  71.         XOFF;
  72.         ResetPars( baud, 1, FALSE, FALSE, 8, ok );
  73.     END XModemCom;
  74.  
  75.     PROCEDURE ResetCom;     (* reinit COM after Xmodem *)
  76.     BEGIN
  77.         ResetPars( baud, 1, parity, evenp, bits, ok );
  78.         IF Xon THEN XON END;
  79.     END ResetCom;
  80.  
  81. BEGIN (* Command *)
  82.     Lock( DisplayLock );
  83.     Concat( "|Parameters|Send Text|Xmit Xmodem|Rcv Xmodem",
  84.             "|Open capFile|Close capFile|Quit",
  85.             cmdstr );
  86.     PopMenu( 5,5, cmdstr, 0,FALSE,cmd);
  87.     CASE cmd OF
  88.         0:  ;
  89.         |
  90.         1:  (* Reconfig *)
  91.             CloseCurWindow; Reconfig
  92.         |
  93.         2:  (* Send text *)
  94.             OpenWindow( w, 5,22, 8,75, TRUE, "" );
  95.             Terminal.WriteString( " File name: " );
  96.             Terminal.ReadLine( fn );
  97.             IF fn[0] <> 0C THEN
  98.                 Open( SendFD, fn, READ );
  99.                 IF SendFD = -1 THEN
  100.                     Terminal.WriteString( " --- cannot open file" );
  101.                     Terminal.Read( c );
  102.                 ELSE
  103.                     Sending := TRUE;
  104.                     SendPtr := 0; BuffEnd := 0;
  105.                     Signal( SendTextSignal );
  106.                 END;
  107.             END;
  108.             CloseCurWindow;
  109.             CloseCurWindow;    (* MENU window *)
  110.         |
  111.         3:  (* Xmit file *)
  112.             OpenWindow( w, 5,22, 8,75, TRUE, "" );
  113.             Terminal.WriteString( " File to send: " );
  114.             Terminal.ReadLine( fn );
  115.             IF fn[0] <> 0C THEN
  116.                 Open( XmodemFD, fn, READ );
  117.                 IF XmodemFD = -1 THEN
  118.                     Terminal.WriteString( " --- cannot open file" );
  119.                     Terminal.Read( c );
  120.                 ELSE
  121.                     XModemCom;
  122.                     SendFile( fn, XmodemFD, done );
  123.                     Close( XmodemFD );
  124.                     ResetCom;
  125.                 END;
  126.             END;
  127.             CloseCurWindow;
  128.             CloseCurWindow;    (* MENU window *)
  129.         |
  130.         4:  (* Rcv file *)
  131.             OpenWindow( w, 5,22, 8,75, TRUE, "" );
  132.             Terminal.WriteString( " File to receive: " );
  133.             Terminal.ReadLine( fn );
  134.             IF fn[0] <> 0C THEN
  135.                 Create( XmodemFD, fn, NORMAL );
  136.                 IF XmodemFD = -1 THEN
  137.                     Terminal.WriteString( " --- cannot create file" );
  138.                     Terminal.Read( c );
  139.                 ELSE
  140.                     XModemCom;
  141.                     ReceiveFile( fn, XmodemFD, done );
  142.                     Close( XmodemFD );
  143.                     ResetCom;
  144.                 END;
  145.             END;
  146.             CloseCurWindow;
  147.             CloseCurWindow;    (* MENU window *)
  148.         |
  149.         5:  (* Open capFile *)
  150.             IF Capturing THEN StopCapture END;
  151.             OpenWindow( w, 5,22, 8,75, TRUE, "" );
  152.             Terminal.WriteString( " File name: " );
  153.             Terminal.ReadLine( fn );
  154.             IF fn[0] <> 0C THEN
  155.                 Create( CaptureFD, fn, NORMAL );
  156.                 IF CaptureFD = -1 THEN
  157.                     Terminal.WriteString( " --- cannot create file" );
  158.                     Terminal.Read( c );
  159.                 ELSE
  160.                     Capturing := TRUE;
  161.                     CapPtr := 0;
  162.                 END;
  163.             END;
  164.             CloseCurWindow;
  165.             CloseCurWindow;    (* MENU window *)
  166.         |
  167.         6:  (* Close capFile *)
  168.             IF Capturing THEN
  169.                 StopCapture;
  170.             END;
  171.             CloseCurWindow;
  172.         |
  173.         7:  (* Quit *)
  174.             CloseCurWindow;
  175.             IF Capturing THEN StopCapture END;
  176.             ScrollUp( 0, 0,0, 25,79, attrNormal );
  177.             Terminate(0);
  178.     END;
  179.     Unlock( DisplayLock );
  180. END Command;
  181.  
  182.  
  183. PROCEDURE Reconfig;
  184. VAR item :CARDINAL;
  185.     cmd :CARDINAL;
  186.     c :CHAR;
  187.     w :Window;
  188.  
  189.     PROCEDURE putBaud;
  190.     BEGIN
  191.         Goto( 1,1 ); WriteString( "Baud Rate >" );
  192.         Goto( 1,15 ); WriteString( "      " );
  193.         Goto( 1,15 ); WriteCard( baud, 1 );
  194.     END putBaud;
  195.  
  196.     PROCEDURE putParity;
  197.     BEGIN
  198.         Goto( 3,1 ); WriteString( "Parity >" );
  199.         Goto( 3,15 );
  200.         IF parity & evenp THEN WriteString( "EVEN" );
  201.         ELSIF parity THEN WriteString( "ODD " );
  202.         ELSE WriteString( "NONE" );
  203.         END;
  204.     END putParity;
  205.  
  206.     PROCEDURE putXon;
  207.     BEGIN
  208.         Goto( 5,1 ); WriteString( "Xon/Off " );
  209.         Goto( 5,15 );
  210.         IF Xon THEN WriteString( "enabled " )
  211.         ELSE WriteString( "disabled" )
  212.         END;
  213.     END putXon;
  214.  
  215. BEGIN (* Reconfig *)
  216.     OpenWindow( w, 0,0, 23,40, TRUE, "Terminal Reconfiguration" );
  217.     putBaud;
  218.     putParity;
  219.     putXon;
  220.     LOOP
  221.         PopMenu( 9,7, "Change|Baud|Parity|Xon/Xoff", 0, FALSE, item );
  222.         IF item = 0 THEN
  223.             EXIT
  224.         ELSE
  225.             CASE item OF
  226.             1:  PopMenu( 10,10, "baud|300|600|1200|2400|4800|9600|19200|38400",
  227.                          0, TRUE, cmd );
  228.                 CloseCurWindow;    (* loop MENU *)
  229.                 IF cmd > 0 THEN
  230.                     baud := 300;
  231.                     WHILE cmd > 1 DO
  232.                         INC( baud, baud );
  233.                         DEC( cmd );
  234.                     END;
  235.                     putBaud;
  236.                 END;
  237.             |
  238.             2:  PopMenu( 11,10, "parity|EVEN|ODD|NONE", 0, TRUE, cmd );
  239.                 CloseCurWindow;    (* loop MENU *)
  240.                 IF cmd > 0 THEN
  241.                     parity := cmd < 3;
  242.                     evenp := cmd = 1;
  243.                     putParity;
  244.                     IF parity THEN bits := 7
  245.                     ELSE bits := 8
  246.                     END;
  247.                 END;
  248.             |
  249.             3:  Xon := NOT Xon;
  250.                 IF Xon THEN XON ELSE XOFF END;
  251.                 CloseCurWindow;    (* loop MENU *)
  252.                 putXon;
  253.             END;
  254.         END;
  255.     END;
  256.     Init( port, baud, 1, parity, evenp, bits, comBuffSize, ok );
  257.     IF NOT ok THEN WriteString( "failed rs232 Init" ); Terminate(1) END;
  258.     CloseCurWindow;
  259. END Reconfig;
  260.  
  261.  
  262.  
  263. CONST BUFFSIZE = 512;
  264.  
  265. VAR
  266.     XmodemFD    :INTEGER;
  267.     SendFD      :INTEGER;
  268.     SendBuff    :ARRAY [0..BUFFSIZE-1] OF CHAR;
  269.     BuffEnd     :CARDINAL;
  270.     SendPtr     :CARDINAL;
  271.  
  272. CONST
  273.     CapBufferSize = 512;
  274.  
  275. VAR
  276.     CaptureFD   :INTEGER;
  277.     CapBuffer   :ARRAY [0..512] OF CHAR;
  278.     CapPtr      :CARDINAL;
  279.  
  280.  
  281. PROCEDURE Capture( c :CHAR );
  282. BEGIN
  283.     CapBuffer[CapPtr] := c;
  284.     INC( CapPtr );
  285.     IF CapPtr >= CapBufferSize THEN
  286.         FlushCaptureBuffer
  287.     END;
  288. END Capture;
  289.  
  290.  
  291. PROCEDURE FlushCaptureBuffer;
  292. VAR n :CARDINAL;
  293. BEGIN
  294.     IF CapPtr > 0 THEN
  295.         Write( CaptureFD, ADR(CapBuffer), CapPtr, n );
  296.         CapPtr := 0;
  297.     END;
  298. END FlushCaptureBuffer;
  299.  
  300.  
  301. PROCEDURE StopCapture;
  302. BEGIN
  303.     FlushCaptureBuffer;
  304.     Close( CaptureFD );
  305.     Capturing := FALSE;
  306. END StopCapture;
  307.  
  308.  
  309. (*PROCESS*) PROCEDURE ReadRS232;
  310. (*
  311.     This process Waits on Signals from the RS232 driver.
  312.     On each signal, we try to process a COM input character.
  313. *)
  314. VAR c   :CHAR;
  315.     ok  :BOOLEAN;
  316.     lockedSend :BOOLEAN;
  317. BEGIN
  318.     lockedSend := FALSE;
  319.     LOOP
  320.         Wait( RS232Input );
  321.         IF Sending THEN
  322.             GetCom( c, ok );
  323.             IF ok THEN
  324.                 IF (c = CtrlS) OR (c = CtrlQ) THEN
  325.                     IF c = CtrlS THEN
  326.                         Lock( SendLock );
  327.                         lockedSend := TRUE;
  328.                     ELSIF c = CtrlQ THEN
  329.                         Unlock( SendLock );
  330.                     END;
  331.                 ELSE
  332.                     IF Capturing THEN Capture( c ) END;
  333.                     Display.Write( c );
  334.                 END;
  335.             END;
  336.         ELSIF lockedSend THEN Unlock( SendLock )
  337.         ELSE
  338.             Lock( DisplayLock );
  339.             GetCom( c, ok );
  340.             IF ok THEN
  341.                 IF Capturing THEN Capture( c ) END;
  342.                 Display.Write( c );
  343.             END;
  344.             Unlock( DisplayLock );
  345.         END;
  346.     END;
  347. END ReadRS232;
  348.  
  349.  
  350. VAR KeyboardInput :SignalHeader;
  351.  
  352. MODULE KeyboardTrap;
  353. (*  We must run with interrupts enabled because the AT's BIOS
  354.     ISR depends on these interrupts to talk to the keyboard!
  355. *)
  356.     IMPORT ASSEMBLER, ADDRESS, TermProcedure,
  357.            GetVector, ResetVector, WaitIO, Signal, KeyboardInput;
  358.  
  359.     EXPORT CheckKeyboard;
  360.  
  361.     VAR KeyboardHandler :ADDRESS;
  362.  
  363.     (*PROCESS*) PROCEDURE CheckKeyboard;
  364.     (*
  365.         This process Signals ReadKbd whenever a keyboard interrupt occurs.
  366.     *)
  367.     BEGIN
  368.         LOOP
  369.             WaitIO( 9 );
  370.             ASM
  371.                 PUSHF
  372.                 CALL    FAR KeyboardHandler
  373.             END;
  374.             Signal( KeyboardInput );
  375.         END;
  376.     END CheckKeyboard;
  377.  
  378.     PROCEDURE restoreKeyboard;
  379.     BEGIN
  380.         ResetVector( 9, KeyboardHandler );
  381.     END restoreKeyboard;
  382.  
  383. BEGIN
  384.     GetVector( 9, KeyboardHandler );
  385.     TermProcedure( restoreKeyboard );
  386. END KeyboardTrap;
  387.  
  388.  
  389. (*PROCESS*) PROCEDURE ReadKbd;
  390. (*
  391.     This process Waits for Signals from CheckKeyboard.
  392.     On a signal, we poll the keyboard for possible input.
  393. *)
  394. VAR i   :CARDINAL;
  395.     c   :CHAR;
  396. BEGIN
  397.     LOOP
  398.         Wait( KeyboardInput );
  399.         WHILE KeyPressed() DO
  400.             (* Because we run the Keyboard Trap w/ interrupts enabled,
  401.                it is possible that more than 1 key was pressed for a
  402.                given signal sent to us.
  403.             *)
  404.             GetKeyCh( c );
  405.             IF Sending THEN
  406.                 IF c = ESC THEN
  407.                     Sending := FALSE;
  408.                     Signal( RS232Input );   (* wake up in case of locked Send *)
  409.                 END;
  410.             ELSIF c = F10 THEN Command
  411.             ELSE
  412.                 PutCom( c );
  413.             END;
  414.         END;
  415.     END;
  416. END ReadKbd;
  417.  
  418.  
  419. VAR SendTextSignal :SignalHeader;
  420.  
  421. (*PROCESS*) PROCEDURE SendText;
  422. (*
  423.     This process Waits on the SendTextSignal.
  424.     On receipt of a signal, the process goes to work sending the
  425.     text file to the remote system.
  426.     During the send loop (WHILE sending), SendLock is used so that
  427.     the ReadRS232 process may communicate the receipt of XOFF and XON
  428.     characters from the other system.
  429.  
  430.     Notice that Sending can be turned off by the ReadKbd process,
  431.     which "shares" this global variable with SendText.
  432. *)
  433. BEGIN
  434.     LOOP
  435.         Wait( SendTextSignal );
  436.         IF Sending THEN
  437.             WHILE Sending DO
  438.                 Lock( SendLock );
  439.                 IF SendPtr >= BuffEnd THEN
  440.                     Read( SendFD, ADR(SendBuff), BUFFSIZE, BuffEnd );
  441.                     SendPtr := 0;
  442.                     Sending := BuffEnd <> 0;
  443.                 END;
  444.                 IF Sending THEN
  445.                     PutCom( SendBuff[SendPtr] );
  446.                     INC( SendPtr );
  447.                 END;
  448.                 Unlock( SendLock );
  449.             END;
  450.             Close( SendFD );
  451.         END;
  452.     END;
  453. END SendText;
  454.  
  455.  
  456. PROCEDURE usage;
  457. BEGIN
  458.     WriteString( "usage: Talk [port# [baud [parity]]]" ); WriteLn;
  459.     WriteString( "       port: 1 | 2" ); WriteLn;
  460.     WriteString( "       baud: 50, 110, 300..38400" ); WriteLn;
  461.     WriteString( "       parity: NONE | EVEN | ODD" ); WriteLn;
  462.     Terminate(1);
  463. END usage;
  464.  
  465.  
  466. (*** Main program starts here ***)
  467.  
  468. VAR
  469.     ok  :BOOLEAN;
  470.     c   :CHAR;
  471.     par :CARDINAL;
  472.     w   :Window;
  473.     arg :ARRAY [0..10] OF CHAR;
  474.     n   :CARDINAL;
  475.  
  476. BEGIN
  477.     Sending := FALSE; Capturing := FALSE;
  478.     GetArg( arg, n );
  479.     IF n > 0 THEN
  480.         StringToCard( arg, port, ok );
  481.         IF NOT ok THEN usage END;
  482.     ELSE port := 1
  483.     END;
  484.     GetArg( arg, n );
  485.     IF n > 0 THEN
  486.         StringToCard( arg, baud, ok );
  487.         IF NOT ok THEN usage END;
  488.     ELSE baud := 1200
  489.     END;
  490.     GetArg( arg, n );
  491.     IF n > 0 THEN
  492.         REPEAT DEC(n); arg[n] := CAP(arg[n])
  493.         UNTIL n = 0;
  494.         IF CompareStr( arg, "EVEN" ) = 0 THEN
  495.             parity := TRUE; evenp := TRUE; bits := 7;
  496.         ELSIF CompareStr( arg, "ODD" ) = 0 THEN
  497.             parity := TRUE; evenp := FALSE; bits := 7;
  498.         ELSIF CompareStr( arg, "NONE" ) = 0 THEN
  499.             parity := FALSE; evenp := FALSE; bits := 8;
  500.         ELSE
  501.             usage
  502.         END;
  503.     ELSE
  504.         parity := FALSE; evenp := FALSE; bits := 8;
  505.     END;
  506.     DisplayLine(
  507.     "    I TALK  (C)Copyright 1987 F S T. All rights reserved.    F10 = Menu",
  508.          25, attrReverse
  509.     );
  510.     OpenWindow( w, 0,0, 23,79, FALSE, "" );
  511.  
  512.     InitSignal( KeyboardInput );
  513.     InitSignal( SendTextSignal );
  514.     InitLock( SendLock );
  515.     InitLock( DisplayLock );
  516.  
  517.     NewProcess( CheckKeyboard, 512, TRUE );
  518.     NewProcess( ReadRS232, 512, FALSE );
  519.     NewProcess( SendText, 512, FALSE );
  520.  
  521.     Init( port, baud, 1, parity, evenp, bits, comBuffSize, ok );
  522.     IF NOT ok THEN Terminate(1) END;
  523.     XON; Xon := TRUE;
  524.  
  525.     ReadKbd;
  526.  
  527. END Talk.